/*
 * Indiana University Community Grid Computing Lab Software License,Version 1.1
 *
 * Copyright (c) 2002 Community Grid Computing Lab, Indiana University.
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 *
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in
 *    the documentation and/or other materials provided with the
 *    distribution.
 *
 * 3. The end-user documentation included with the redistribution,
 *    if any, must include the following acknowledgment:
 *      "This product includes software developed by the Indiana University
 *       Community Grid Computing Lab (http://www.communitygrids.iu.edu/)."
 *    Alternately, this acknowledgment may appear in the software itself,
 *    if and wherever such third-party acknowledgments normally appear.
 *
 * 4. The names "Indiana Univeristy","Indiana Univeristy Pervasive Techonology
 *    Labs" and  "Indiana Univeristy Community Grid Computing Lab" must not be
 *    used to endorse or promote products derived from this software without
 *    prior written permission. For written permission, please contact
 *    http://www.communitygrids.iu.edu/.
 *
 * 5. Products derived from this software may not use "Indiana
 *    University" name nor may "Indiana University" appear in their name,
 *    without prior written permission of the Indiana University.
 *
 *
 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES,
 * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
 * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
 * INDIANA UNIVERSITY OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
 * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
 * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */
package cgl.sensorgrid.sopac.gps.filters;


import java.nio.*;
import java.text.*;
import java.util.*;

import cgl.narada.event.*;
import cgl.narada.util.*;
import cgl.sensorgrid.common.*;
import cgl.sensorgrid.filters.*;
import cgl.sensorgrid.sopac.gps.threadpool.*;


/**
 *
 * <p>Title: Sensor Grid</p>
 *
 * <p>Copyright: Copyright (c) 2005</p>
 *
 * <p>Company: CGL</p>
 *
 * @author Galip Aydin
 * @version 1.0
 */

class ryo2asciiFilter extends Thread {

    protected cgl.sensorgrid.sopac.gps.threadpool.Done done;
    private NBEvent ryo;
    private String header = "";
    private String position = "";
    private String xyzVariance = "";
    private String troposphericCovariance = "";
    private String satelliteInfo = "";
    private String RYOmessage = "";

    int[] a = new int[1];
    private ryo2ascii publisher;

    public ryo2asciiFilter(NBEvent ryo, ryo2ascii publisher) {
        try {
            this.publisher = publisher;
            this.ryo = ryo;
            a[0] = 1;
        } catch (Exception ex) {
            ex.printStackTrace();
        }
    }
//
//    /**
//     *
//     * @param done
//     */
//    ryo2asciiFilter(NBEvent _ryo) {
//        ryo = _ryo;
//        //count++;
//    }

    public void run() {
        byte[] data2 = ryo.getContentPayload();
        if (data2 != null && data2.length > 0) {
            processRawMessage(data2);
//            System.gc();
        }
    }


    /**
     *
     * @param input byte[]
     */
    public void processRawMessage(byte[] input) {
        try {
            boolean validHeader = false;
            if (input.length >= 5) {
                validHeader = decodeHeader(input);
            }
            if (validHeader && input.length > 52) {
                decodePosition(input, 5);
            }

            String pos = this.getPosition();
            if (pos.endsWith("\n")) {
                pos = pos.substring(0, pos.length() - 1);
            }
            if (pos != null && pos.trim() != "" && pos.trim().length() > 0) {
                publisher.publishData((pos + "\nEOF\n").getBytes());
            }
            this.position = "";

        } catch (Exception ex) {
            ex.printStackTrace();
        }
    }

    /**
     *
     * @param message String
     * @return String
     */
    public String messageToGML(String message) {
        try {
//            MakeSopacObservationCollection gml = new
//                                                 MakeSopacObservationCollection();
//            return gml.makeSOPACObservationCollection(message);
            return "";
        } catch (Exception ex) {
            ex.printStackTrace();
            return null;
        }
    }


    /**
     *
     * @param input byte[]
     * @return boolean
     */
    public boolean decodeHeader(byte[] input) {
        try {
            boolean validHeader = false;
            cgl.narada.util.ByteUtilities b = new cgl.narada.util.ByteUtilities(
                    a);
            int SyncMarker1 = b.getIntFromByte(input[0]);
            int SyncMarker2 = b.getIntFromByte(input[1]);
            if (Integer.toHexString(SyncMarker1).toUpperCase().equalsIgnoreCase(
                    "9C") &&
                Integer.toHexString(SyncMarker2).toUpperCase().equals("A5")) {
                validHeader = true;
            }
            return validHeader;
        } catch (Exception ex) {
            ex.printStackTrace();
            return false;
        }
    }

    /**
     *
     * @param input byte[]
     * @param offset int
     */
    public String decodePosition(byte[] input, int offset) {
        try {
            String line = "";
            ByteUtilities b = new ByteUtilities(a);
            int i = offset;
            ByteBuffer bb = ByteBuffer.wrap(input);
            bb.order(ByteOrder.LITTLE_ENDIAN);

            //Date
            short GPSWeek = bb.getShort(i);
            int GPSmsOfWeek = bb.getInt(i + 2);
            String date = getDate(Short.toString(GPSWeek),
                                  Integer.toString(GPSmsOfWeek));
            //Site info
            int SiteIndex = b.getIntFromByte(input[i + 6]);
            int SiteCount = b.getIntFromByte(input[i + 7]);
            String SiteId = Character.toString((char) (input[i + 8])) +
                            Character.toString((char) (input[i + 9])) +
                            Character.toString((char) (input[i + 10])) +
                            Character.toString((char) (input[i + 11]));

            //Skip 12 and 13
            //            line += " " + GPSWeek;
            //            line += " " + GPSmsOfWeek;
            line += "\t" + date;
            line += "\t" + SiteId;
            line += "\t" + SiteIndex;
            line += "\t" + SiteCount;

            //ITRF Positions
            double X = bb.getDouble((i + 14));
            double Y = bb.getDouble((i + 22));
            double Z = bb.getDouble((i + 30));
            double T = bb.getDouble((i + 38));
            line += "\t" + X;
            line += "\t" + Y;
            line += "\t" + Z;
            line += "\t" + T;
            //            System.out.println("X " + X);
            //            System.out.println("Y " + Y);
            //            System.out.println("Z " + Z);
            //            System.out.println("T " + T);


            //Lat, Lon, Height positions
            double lat = 0.0;
            double lon = 0.0;
            double hi = 0.0;

            ITRFtoLatLon latlon = new ITRFtoLatLon();
            double[] llh = null;
            llh = latlon.GetLatLonHeightRadians(X, Y, Z);

            if (llh != null && llh.length == 3) {
                line += "\t" + llh[0];
                line += "\t" + llh[1];
                line += "\t" + llh[2];
            }

            String posQualityBytes = b.printByte(input[i + 46]);
            if (!posQualityBytes.equals("") && posQualityBytes.length() == 8) {
                String method = posQualityBytes.substring(3, 6);
                String calc = posQualityBytes.substring(6, 8);

                if (calc.equals("00")) {
                    line += " Undetermined";
                } else if (calc.equals("01")) {
                    line += " L1";
                } else if (calc.equals("10")) {
                    line += " L1/L2";
                }

                if (method.equals("001")) {
                    line += " Undetermined";
                } else if (method.equals("010")) {
                    line += " Absolute";
                } else if (method.equals("011")) {
                    line += " Relative";
                } else if (method.equals("100")) {
                    line += " Phase";
                }
            }

            //int Flag = b.getIntFromByte(input[i + 47]);
            String flagString = b.printByte(input[i + 47]);
            //line += " " + flagString;
            boolean xyz = false;
            boolean tropo = false;
            boolean satellite = false;

            if (!flagString.equals("") && flagString.length() == 8) {
                if (flagString.charAt(7) == '1') {
                    line += " XYZ";
                    xyz = true;
                }
                if (flagString.charAt(6) == '1') {
                    line += " Troposhpheric";
                    tropo = true;
                }
                if (flagString.charAt(5) == '1') {
                    line += " Satellite";
                    satellite = true;
                }
            }

            i += 47;

            if (input.length > 51) {
                if (xyz == true) {
                    i = decodeXYZVariances(input, i + 1);
                }
                line += " " + this.getXyzVariance();
                this.setXyzVariance("");
            }

            if (input.length > i) {
                if (satellite == true) {
                    i = decodeSatellite(input, i + 1);
                }
                line += " " + this.getSatelliteInfo();
                this.setSatelliteInfo("");
            }

            if (line != null && !line.equals("")) {
                this.appendPosition(line.trim() + "\n");
            }

            if (input.length > i + 52) {
                decodePosition(input, i + 7);
            }

            return line;
        } catch (Exception ex) {
            System.err.println("\nException occured in decodePosition()");
            ex.printStackTrace();
            return null;
        }
    }


    /**
     *
     * @param input byte[]
     * @param i int
     * @return int
     */
    public int decodeXYZVariances(byte[] input, int i) {
        try {
            String line = "";
            ByteBuffer bb = ByteBuffer.wrap(input);
            bb.order(ByteOrder.LITTLE_ENDIAN);

            double scale = bb.getDouble(i);
            double Xvar = bb.getDouble((i + 8));
            double Yvar = bb.getDouble((i + 16));
            double Zvar = bb.getDouble((i + 24));
            double YXvar = bb.getDouble((i + 32));
            double YZvar = bb.getDouble((i + 40));
            double ZXvar = bb.getDouble((i + 48));

            i += 55;
            line += " " + scale;
            line += " " + Xvar;
            line += " " + Yvar;
            line += " " + Zvar;
            line += " " + YXvar;
            line += " " + YZvar;
            line += " " + ZXvar;
            this.setXyzVariance(line);
            return i;
        } catch (Exception ex) {
            //            System.err.println("\nException occured in decodeVariances()");
            return i;
        }

    }

    public int decodeTroposphericCoverianceBlock(byte[] input, int i) {
        try {
            String line = "";
            ByteBuffer bb = ByteBuffer.wrap(input);
            bb.order(ByteOrder.LITTLE_ENDIAN);

            double Tvar = bb.getDouble((i + 8));
            double TXcovar = bb.getDouble((i + 16));
            double TYcovar = bb.getDouble((i + 24));
            double TZcovar = bb.getDouble((i + 32));

            i += 33;
            line += " " + Tvar;
            line += " " + TXcovar;
            line += " " + TYcovar;
            line += " " + TZcovar;
            this.setTroposphericCovariance(line);
            return i;
        } catch (Exception ex) {
            //            System.err.println("\nException occured in decodeVariances()");
            return i;
        }

    }

    public int decodeSatellite(byte[] input, int i) {
        try {
            String line = "";
            ByteUtilities b = new ByteUtilities(a);
            ByteBuffer bb = ByteBuffer.wrap(input);
            bb.order(ByteOrder.LITTLE_ENDIAN);

            int satelliteCount = b.getIntFromByte(input[i]);
            // line += " " + satelliteCount;
            double GDOP = bb.getDouble((i + 1));
            // line += " " + GDOP;

            if (satelliteCount > 0) {
                i += 9;
                for (int j = 0; j < satelliteCount; j++) {
                    if (input.length > i + 5) {
                        int PRN = b.getIntFromByte(input[i]);
                        int PRNFlags = b.getIntFromByte(input[i + 1]);
                        short Elevation = bb.getShort(i + 2);
                        short Azimuth = bb.getShort(i + 4);
                        line += " " + (j + 1);
                        line += " " + PRN;
                        line += " " + PRNFlags;
                        line += " " + Elevation;
                        line += " " + Azimuth;
                        i += 6;
                    }
                }
                this.setSatelliteInfo(line);
            }
            return i;
        } catch (Exception ex) {
            //            System.err.println("\nException occured in decodeSatellite()");
            //            ex.printStackTrace();
            return i;
        }
    }


    public void setHeader(String header) {
        this.header = header;
    }

    public void setPosition(String position) {
        this.position = position;
    }

    public void appendPosition(String pos) {
        this.position += pos;
    }


    public void setXyzVariance(String xyzVariance) {
        this.xyzVariance = xyzVariance;
    }

    public void setTroposphericCovariance(String troposphericCovariance) {
        this.troposphericCovariance = troposphericCovariance;
    }

    public void setSatelliteInfo(String satelliteInfo) {
        this.satelliteInfo = satelliteInfo;
    }

    public void setRYOmessage(String header, String position) {
        String RYOmsg = header;
        RYOmsg.trim();

        RYOmsg += position;
        RYOmsg.trim();
        this.RYOmessage = RYOmsg;
    }

    public String getHeader() {
        return header;
    }

    public String getPosition() {
        return position;
    }

    public String getXyzVariance() {
        return xyzVariance;
    }

    public String getTroposphericCovariance() {
        return troposphericCovariance;
    }

    public String getSatelliteInfo() {
        return satelliteInfo;
    }

    public String getRYOmessage() {
        return RYOmessage;
    }

    /**
     * Calculates Gregorian Date from GPS Week and GPS milliseconds
     * @param GPS_week String
     * @param GPS_millisecond String
     * @return String
     */
    public String getDate(String GPS_week, String GPS_millisecond) {
        try {
            //GPS_epoch = "1/6/1980";
            long GPS_start_epoch = 315964800;
            long GPS_leapseconds = 13;

            long time = GPS_start_epoch +
                        Long.parseLong(GPS_week) * 7 * 24 * 3600 +
                        Long.parseLong(GPS_millisecond) / 1000 -
                        GPS_leapseconds;

            Calendar cal = Calendar.getInstance();
            cal.setTimeInMillis(time * 1000);
            SimpleDateFormat myDateFormat =
                    new SimpleDateFormat("yyyy-MM-dd\thh:mm:ssa-z");

            return myDateFormat.format(cal.getTime());
        } catch (Exception ex) {
            ex.printStackTrace();
            return null;
        }
    }
}


/**
 *
 * <p>Title: Sensor Grid</p>
 *
 * <p>Description: A Grid Architecture for combining GIS services with
 * Scientific Visualization and Simulation codes</p>
 *
 * <p>Copyright: Copyright (c) 2005</p>
 *
 * <p>Company: CGL</p>
 *
 * @author Galip Aydin
 * @version 1.0
 */
public class ryo2ascii extends Filter {

    private static ThreadPool pool = new ThreadPool(10);

//    private static ryo2ascii instance = new ryo2ascii();
//
//    public static ryo2ascii getInstance() {
//        return instance;
//    }

    public ryo2ascii() {
    }

    public ryo2ascii(String[] args, String[] filterSpecificArgs) {
        if (args == null || args.length == 0) {
            System.err.println(
                    "Incorrect number of arguments in ryo2ascii::Constructor...");
            System.exit(0);
        } else {
            this.initialize(args);
        }
    }

    /**
     *
     * @param nbEvent NBEvent
     */
    public void onEvent(NBEvent nbEvent) {
        if (nbEvent.getContentPayload() != null) {
            if (nbEvent != null) {
//                System.out.println("Sending");
                //System.out.print(new String( nbEvent.getContentPayload()));
                pool.assign(new ryo2asciiFilter(nbEvent, this));
            }
        }
    }


    public static void main(String args[]) {
        if (args.length < 5) {
            System.out.println("Usage: java cgl.sensorgrid.sopac.gps.filters.ryo2ascii commType nbHost nbPort subTopic pubTopic");
            System.exit(0);
        }
        String commType = args[0];
        String nbHost = args[1];
        String nbPort = args[2];
        String subTopic = args[3];
        String pubTopic = args[4];
        String[] argsa = {commType,
                         nbHost,
                         nbPort,
                         subTopic,
                         pubTopic};

//        String[] argsa = {"niotcp",
//                         "gf2.ucs.indiana.edu",
//                         "3045",
//                         "/SOPAC/GPS/CRTN_01/RYO",
//                         "/SOPAC/GPS/CRTN_01/ASCII"};

        ryo2ascii test = new ryo2ascii(argsa, null);
    }
}
